0%

MySQL Binlog(六)——MySQL常见binlog event解析(中)

MySQL Binlog(六)——MySQL常见binlog event解析(中)

前言

本文继续介绍binlog中常见的event:Table_map_event、Write_rows_log_event、Update_rows_log_event、Delete_rows_log_event、Xid_event。

Table_map_event

TABLE_MAP_EVENT只有在binlog文件是以ROW格式记录的时候,才会使用。binlog中记录的每个更改的记录之前都会有一个对应要操作的表的TABLE_MAP_EVENT。TABLE_MAP_EVENT中记录了表的定义(包括database name,table name,字段定义),并且会将这个表的定义对应于一个数字,称为table_id。设计TABLE_MAP_EVENT类型event的目的是为了当主库和从库之间有不同的表定义的时候,复制仍能进行。如果一个事务中操作了多个表,多行记录,在binlog中会将对多行记录的操作event进行分组,每组行记录操作event前面会出现对应表的TABLE_MAP_EVENT。

post-header部分

字段 字节数 描述
table_id 6字节 操作的表的table_id
flags 2字节 目前版本没有用,都是0,保留给之后的版本使用

event-body部分

字段 字节数 描述
database_name 1个字节表示字符串长度,之后接一个null-terminated类型的字符串 操作的数据库的名称
table_name 1个字节表示字符串长度,之后接一个null-terminated类型的字符串 操作的表的名称
column_count packed integer(1或3或4或9个字节) 对应表中的字段数量
column_type 每个字段占用一个字节,字段类型定义在enum_field_types中 字段类型
metadata_length packed integer(1或3或4或9个字节) 对应字段的元数据信息的长度
metadata 根据enum_field_type中的定义确定不同字段的元数据,如果某字段类型没有元数据,则不记录 每个字段的元数据信息,比如varchar字段需要记录最长长度
null_bits int((column_count + 7)/8) 字节 一个bit表示一个字段是否可以为NULL,顺序是:第一个字节的最低位开始向最高位增长,之后第二个字节的最低位开始向最高位增长,以此类推
check_sum 4字节 校验码

字节解析示例

建表语句:

1
2
3
4
5
CREATE TABLE `test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

二进制内容解析:

1
2
3
4
5
6
7
8
9
10
11
公共头部部分省略
6c 00 00 00 00 00 //table_id :小端存储,16进制转换为10进制为108
01 00 //flag :
08 67 61 6e 67 73 68 65 6e 00 //database_name :1个字节是字符串长度,后接一个null-terminated string 第一个字节表示字符串长度为8,后面内容将16进制转换为ascii字符为gangshen
05 74 65 73 74 31 00 //table_name: 1个字节是字符串长度,后接一个null-terminated string第一个字节表示字符串长度为5,后面内容将16进制转换为ascii字符为test1
02 //columns count :packet integer类型,转换之后,数值为2 表示表中有两个字段
03 0f //column type : 一个字节表示一个字段的类型,字段类型定义在enum enum_field_types ,分别是一个int类型以及一个varchar类型,具体的字段类型及记录格式等信息可以查看《MySQL中Rows_event中字段表示》文档
02 //metadata length : packet integer类型,转换之后,数值为2,表示记录表中的metadata内容占用2个字节
14 00 // :varchar 的max length 因为int没有metadata所以跳过
02 //null_bits :int((column_count + 7) / 8)个字节 一个bit表示一个字段是否可以为null
52 53 4a d9 checksum

Write_rows_log_event

在以ROW格式记录的binlog文件中,Write_rows_log_event记录了插入的行记录。

post-header部分

字段 字节数 描述
table_id 6字节 操作的表的table_id
flags 2字节 目前版本没有用,都是0,保留给之后的版本使用

event-body部分

字段 字节数 描述
var_header 2字节
m_width packed integer(1或3或4或9个字节) 表中字段数量
before_image (m_width + 7)/8字节
after_bitmap_bits (m_with +7)/8字节 字段值是否为空标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位增长,之后第二个字节的最低位开始向最高位增长,以此类推
after_column_content 不定 按照顺序每个字段的内容
check_sum 4字节 校验码

字节解析示例

建表语句

1
2
3
4
5
6
7
8
CREATE TABLE `int_table` (
`col1` tinyint(4) DEFAULT NULL,
`col2` smallint(6) DEFAULT NULL,
`col3` mediumint(9) DEFAULT NULL,
`col4` int(11) DEFAULT NULL,
`col5` bigint(20) DEFAULT NULL,
`col6` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

插入数据:

1
insert into int_table values(1,11,111,1111,11111,true);

对应Write_rows_log_event使用mysqlbinlog解析

1
2
3
4
5
6
7
8
9
10
11
# at 340
#180103 10:21:20 server id 330619 end_log_pos 395 CRC32 0x50177e10 Write_rows: table id 100 flags: STMT_END_F
### INSERT INTO `gangshen`.`int_table`
### SET
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=11 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=111 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 395

解析二进制文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
公共头部部分省略
64 00 00 00 00 00 //table_id: 小端存储,16进制转换为10进制为100
01 00 //flag:
02 00 //var header length :小端存储,16进制转换为10进制为2
06 //m_width :packet integer,表示表中字段数量
ff //before image: (m_width + 7) / 8字节
c0 //after_bitmap_bits :insert插入之后的记录的NULL标记,表中六个字段,插入的值都不为NULL
//after_columns_content:insert插入记录之后的记录值
01
0b 00
6f 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
10 7e 17 50 //checksum

Update_rows_log_event

在以ROW格式记录的binlog文件中,Update_rows_log_event记录了更新的行记录。

post-header部分

字段 字节数 描述
table_id 6字节 操作的表的table_id
flags 2字节 目前版本没有用,都是0,保留给之后的版本使用

event-body部分

字段 字节数 描述
var_header 2字节
m_width packed integer(1或3或4或9个字节) 表中字段数量
before_image (m_with+7)/8字节
after_image (m_with+7)/8字节
before_bitmap_bits (m_with+7)/8字节 before_bitmap_bits记录的是update更改之前的记录中,字段值是否为NULL标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位开始增长,之后第二个字节的最低位开始向最高位增长,以此类推
before_column_content 不定 update更新之前的记录值,按照顺序每个字段的内容
after_bitmap_bits (m_with+7)/8字节 after_bitmap_bits记录的是update更改之前的记录中,字段值是否为NULL标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位开始增长,之后第二个字节的最低位开始向最高位增长,以此类推
after_column_content 不定 update更新之后的记录值,按照顺序每个字段的内容
check_sum 4字节 校验码

字节解析示例

建表语句

1
2
3
4
5
6
7
8
CREATE TABLE `int_table` (
`col1` tinyint(4) DEFAULT NULL,
`col2` smallint(6) DEFAULT NULL,
`col3` mediumint(9) DEFAULT NULL,
`col4` int(11) DEFAULT NULL,
`col5` bigint(20) DEFAULT NULL,
`col6` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

插入数据并更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@localhost : gangshen 10:20:49> insert into int_table values(1,11,111,1111,11111,true);
Query OK, 1 row affected (0.03 sec)

root@localhost : gangshen 10:21:20> update int_table set col2=22,col3=222 where col1=1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

root@localhost : gangshen 10:21:44> select * from int_table;
+------+------+------+------+-------+------+
| col1 | col2 | col3 | col4 | col5 | col6 |
+------+------+------+------+-------+------+
| 1 | 22 | 222 | 1111 | 11111 | 1 |
+------+------+------+------+-------+------+
1 row in set (0.00 sec)

对应Update_rows_log_event使用mysqlbinlog解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# at 644
#180103 10:41:37 server id 9999 end_log_pos 720 CRC32 0x5576b52f Update_rows: table id 231 flags: STMT_END_F
### UPDATE `gangshen`.`int_table`
### WHERE
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=11 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=111 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### SET
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=22 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=222 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 720

解析二进制文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
公共头部部分省略。。。
64 00 00 00 00 00 //table_id: 小端存储,16进制转换为10进制为100
01 00 //flag:
02 00 //var header length :小端存储,16进制转换为10进制为2
06 //m_width :packet integer,表示表中字段数量
ff //before image: (m_width + 7) / 8字节
ff //after image: (m_width + 7) / 8字节
c0 //before_bitmap_bits :update更新之前记录中NULL标记,表中六个字段,值都不为NULL
//before_column_content接下来是update更新之前记录的值
01
0b 00
6f 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
c0 //after_bitmap_bits :update更新之后记录中NULL标记,表中六个字段,值都不为NULL
//after_column_content接下来是update更新之前记录的值
01
16 00
de 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
16 a3 de bb //checksum

Delete_rows_log_event

在以ROW格式记录的binlog文件中,Delete_rows_log_event记录了删除的行记录。

post-header部分

字段 字节数 描述
table_id 6字节 操作的表的table_id
flags 2字节 目前版本没有用,都是0,保留给之后的版本使用

event-body部分

字段 字节数 描述
var_header 2字节
m_width packed integer(1或3或4或9个字节) 表中字段数量
after_image (m_width + 7)/8字节
before_bitmap_bits (m_with+7)/8字节 before_bitmap_bits记录的是update更改之前的记录中,字段值是否为NULL标记,一个bit表示一个字段是否为NULL,顺序是:第一个字节的最低位开始向最高位开始增长,之后第二个字节的最低位开始向最高位增长,以此类推
before_column_content 不定 delete删除之前的记录值,按照顺序每个字段的内容
check_sum 4字节 校验码

字节解析示例

建表语句

1
2
3
4
5
6
7
8
CREATE TABLE `int_table` (
`col1` tinyint(4) DEFAULT NULL,
`col2` smallint(6) DEFAULT NULL,
`col3` mediumint(9) DEFAULT NULL,
`col4` int(11) DEFAULT NULL,
`col5` bigint(20) DEFAULT NULL,
`col6` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

删除数据:

1
delete from int_table where col1=1;

对应Delete_rows_log_event使用mysqlbinlog解析

1
2
3
4
5
6
7
8
9
10
11
# at 320
#180103 14:24:54 server id 330619 end_log_pos 375 CRC32 0xce4818b2 Delete_rows: table id 100 flags: STMT_END_F
### DELETE FROM `gangshen`.`int_table`
### WHERE
### @1=1 /* TINYINT meta=0 nullable=1 is_null=0 */
### @2=22 /* SHORTINT meta=0 nullable=1 is_null=0 */
### @3=222 /* MEDIUMINT meta=0 nullable=1 is_null=0 */
### @4=1111 /* INT meta=0 nullable=1 is_null=0 */
### @5=11111 /* LONGINT meta=0 nullable=1 is_null=0 */
### @6=1 /* TINYINT meta=0 nullable=1 is_null=0 */
# at 375

解析二进制文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
公共头部部分省略
64 00 00 00 00 00 //table_id: 小端存储,16进制转换为10进制为100
01 00 //flag:
02 00 //var header length :小端存储,16进制转换为10进制为2
06 //m_width :packet integer,表示表中字段数量
ff //after image: (m_width + 7) / 8字节
c0 //before_bitmap_bits :delete删除之前的记录的NULL标记,表中六个字段,值都不为NULL
//before_columns_content:delete删除记录之前的记录值
01
16 00
de 00 00
57 04 00 00
67 2b 00 00 00 00 00 00
01
10 7e 17 50 //checksum

Xid_event

表示支持内部XA的存储引擎上的事务提交。正常的事务是通过QUERY_EVENT来发送一个BEGIN语句并且通过QUERY_EVENT来发送一个COMMIT语句(如果事务回滚则发送的是ROLLBACK)实现。

post-header部分

event-body部分

字段 字节数 描述
xid 8字节 xid号,小端存储,无符号数
check_sum 4字节 校验码

字节解析示例

使用mysqlbinlog工具解析对应的Xid_event

1
2
3
4
# at 1691
#180103 15:30:45 server id 330619 end_log_pos 1722 CRC32 0x8816181c Xid = 2698
COMMIT/*!*/;
# at 1722

解析二进制文件:

1
2
3
公共头部部分省略...
8a 0a 00 00 00 00 00 00 //xid,因为是小端存储,所以实际为0x 00 00 00 00 00 00 0a 8a,转换为10进制为2698
1c 18 16 88 //check_sum